// 浅拷贝
let a = {
  msg: 'hello'
}

let b = {
  name: 'miss'
}

Object.assign(a, b)
console.log("a:", a, "b:", b);
// a: { msg: 'hello', name: 'miss' } b: { name: 'miss' }
a.name = "tree"
console.log("a:", a, "b:", b);
// a: { msg: 'hello', name: 'tree' } b: { name: 'miss' }


let c = {
  num: 3,
  boolean: false,
  fn: function () {
    return 3
  },
  arr: ['1', '2', '3'],
  obj: {
    deep: 'hei'
  }
}

Object.assign(a, c)
c.obj.depp = "change"
c.num = 5
c.boolean = true
c.arr = ['1', '3']
c.fn = function () {
  return 6
}
console.log("a:", a, "c:", c);
/** a: {
msg: 'hello',
  name: 'tree',
  num: 3,
  boolean: false,
  fn: [Function: fn],
  arr: ['1', '2', '3'],
  obj: { deep: 'hei', depp: 'change' }
}
c: {
  num: 5,
  boolean: true,
  fn: [Function],
  arr: ['1', '3'],
  obj: { deep: 'hei', depp: 'change' }
}
*/

/**
 * 从上面简单的示例可以看出
 * 1、对于基本数据都可以做到直接深拷贝的，因为引用的是栈，而不是堆，不是对地址的引用
 * 2、对于Object、Function、symbol、Set、Map、Date、RegExp等引用类型数据，不能拷贝是因为还是引用了地址
 * 地址内的数据改变还是会跟着改变。要实现深拷贝就要创建一个数据内存地址
 * 3、常用的 JSON.parese(JSON.stringfy(obj)) 方法可以解决 95% 的业务数据深拷贝，但是没有解决 Function、symbol、Date、RegExp
 * 数据格式正常深拷贝，还有一些是 JSON 内部的一些原因会将空字符串，+!variable 进行隐式转换
 * 4、lodash 库 cloneDeep 方法，但是内部光调用的方法都80行代码，还不连上引用的方法的代码，不便于记忆或者不是面试的最好方法
 */

// vue-router 拷贝方式
function clone (value) {
  if (Array.isArray(value)) {
    return value.map(clone)
  } else if (value && typeof value === 'object') {
    var res = {};
    for (var key in value) {
      res[key] = clone(value[key]);
    }
    return res
  } else {
    return value
  }
}

// 面试终极代码 

const complexType = (obj) => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)

const deepClone = (obj, hash = new WeakMap()) => {
  if (hash.has(obj)) return hash.get(obj) // 若在 WeakMap 中找到，则直接返回 cloneObj
  let type = [Date, RegExp, Set, Map, WeakMap, WeakSet] // 对于这些类型不做处理，直接 new 实例化
  if (type.includes(obj.constructor)) return new obj.constructor(obj)

  let allDesc = Object.getOwnPropertyDescriptors(obj) // 遍历传入参数所有键特性
  let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
  hash.set(obj, cloneObj) //保存键值对

  for (const key of Reflect.ownKeys(obj)) {// Reflect.owns可以拷贝不可枚举属性和符号类型
    // 如果值是引用类型(非函数)则递归
    cloneObj[key] = (complexType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key], hash) : obj[key]
  }

  return cloneObj
}



